package JDBC;

import java.sql.*;
import java.util.Scanner;

// 连接数据库获取账号和密码登录
public class Test06 {
    public static void main(String[] args) {
        Connection conn = null;
//        Statement stmt = null;
        ResultSet rs = null;
        PreparedStatement ps = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true";
            String username = "root";
            String password = "991025";
            conn = DriverManager.getConnection(url, username, password);
            Scanner scanner = new Scanner(System.in);
            System.out.print("请输入用户名: ");
            String un = scanner.nextLine();
            System.out.print("请输入密 码: ");
            String pw = scanner.nextLine();

            // 下面代码存在 sql 注入问题
//            String sql = "select * from user where name = '" + un
//                    + "' and password = '" + pw + "'";
//            stmt = conn.createStatement();
//            // 下面这个代码的含义为: 将sql语句给DBMS, DBMS进行编译
//            // 是先提交后编译, 所以如果用户提供了非法信息, 就导致了 sql 语句含义被扭曲
//            // 从而出现不符合用户需求的情况
//            rs = stmt.executeQuery(sql);

            // 改进
            // sql 注入是因为用户提供的信息也参与编译了
            // 只要用户的信息不参与编译, 问题就解决了
            // 数据库操作对象使用 PreparedStatement
            // 这个对象属于预编译的数据库操作对象
            // 原理是预先对 sql 语句的框架进行编译, 然后再给 sql 语句进行传值
            // 下面的字符串为 sql 语句的框架 其中?为占位符
            // 一个?将来接受一个值, 注意 占位符不能用单引号引起来
            String sql = "select * from user where name = ? and password = ?";
            // 下面一行执行完, DBMS会将框架先编译好
            ps = conn.prepareStatement(sql);
            // 下面给?传值, 第1个?下标为1, 第2个?下标为2, JDBC 中的下标都是从1开始的
            // setSting(index, str) 为,为第index个?获取字符串str填入,会在传入的字符串两边加上单引号
            // 所以不用?两边不用加单引号
            // setInt(index, int) 为获取int型的数据
            ps.setString(1, un);
            ps.setString(2, pw);
            // 执行 sql 语句, 下面的函数就不用传入 sql 语句了, 因为此时的数据库操作对象中已经有了sql的信息
            rs = ps.executeQuery();


            // PreparedStatement 和 Statement 比较
            // PreparedStatement 解决了注入问题, 而 Statement 没有解决注入问题
            // PreparedStatement 编译一次可以执行多次, 而 Statement 编译一次执行一次
            // PreparedStatement 会在编译期做类型的安全检查, 而 Statement 不会做类型的安全检查
            // 例如 setString(1, 100); 编译期就会报错
            // 综上所述, 大部分情况使用 PreparedStatement
            // 只有极少数情况下需要使用 Statement , 比如就是需要sql注入的时候, 就是要进行 sql 语句拼接的时候
            // 比如, 淘宝中的产品升序或者降序排列, 我们需要使用关键字 desc , 这里如果使用?时不会将这个关键字编译, 就会出现问题


            if (rs.next()) {
                System.out.println("登录成功");
            }else {
                System.out.println("登录失败");
            }

        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
//                if (stmt != null) {
//                    stmt.close();
//                }
                if (ps != null) {
                    ps.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}
